//
//  AHTempString.swift
//  AttributedStringHelper
//
//  Created by JY_NEW on 2022/9/3.
//

import Foundation

public class AHInterpolationString: ExpressibleByStringInterpolation, StringInterpolationProtocol {
    
    public typealias StringLiteralType = String
    
    public indirect enum Component {
        case string(String, [AHSetting]?)
        case components([Component], [AHSetting]?)
        public mutating func append(_ component: Component) {
            switch self {
            case .string(let string, let settings):
                self = .components([.string(string, nil), component], settings)
            case .components(let array, let settings):
                self = .components(array + [component], settings)
            }
        }
        public mutating func append(_ settings: [AHSetting]) {
            switch self {
            case .string(let string, let oldSettings):
                self = .string(string, settings + (oldSettings ?? []))
            case .components(let cs, let oldSettings):
                self = .components(cs, settings + (oldSettings ?? []))
            }
        }
        public var string: String {
            switch self {
            case .string(let text, _): return text
            case .components(let cs, _): return cs.reduce("", { $0 + $1.string })
            }
        }
        public func settings(for startIndex: Int = 0) -> [(NSRange, [AHSetting]?)]? {
            switch self {
            case .string(let text, let settings):
                return [(NSMakeRange(startIndex, text.count), settings)]
            case .components(let cs, let settings):
                var index = startIndex
                var result: [(NSRange, [AHSetting]?)] = []
                for c in cs {
                    if let r = c.settings(for: index), let first = r.first {
                        result.append(contentsOf: r)
                        index += first.0.length
                    }
                }
                if index > startIndex {
                    result.insert((NSMakeRange(startIndex, index - startIndex), settings), at: 0)
                }
                return result.isEmpty ? nil : result
            }
        }
    }
    
    public var component: Component
    
    public var attributedString: NSMutableAttributedString {
        let result = NSMutableAttributedString(string: self.component.string)
        self.component.settings()?.forEach {
            if let attrs = $0.1?.attributes() {
                result.addAttributes(attrs, range: $0.0)
            }
        }
        return result
    }
    public init(_ string: String) {
        self.component = .string(string, nil)
    }
    public init(string: String, settings: [AHSetting]?) {
        self.component = .string(string, settings)
    }
    required public convenience init(stringLiteral value: String) {
        self.init(value)
    }
    required public init(stringInterpolation: AHInterpolationString) {
        self.component = stringInterpolation.component
    }
    required public init(literalCapacity: Int, interpolationCount: Int) {
        self.component = .string("", nil)
    }
    
    public func append(settings: [AHSetting]) {
        self.component.append(settings)
    }
    
    public func appendLiteral(_ literal:String) {
        self.component.append(.string(literal, nil))
    }

    public func appendInterpolation(string literal:String, _ settings: AHSetting...) {
        self.component.append(.string(literal, settings))
    }
    public func appendInterpolation(_ string:AHInterpolationString, _ settings: AHSetting...) {
        var component = string.component
        component.append(settings)
        self.component.append(component)
    }
    public func appendInterpolation(attachment:NSTextAttachment) {
        self.component.append(.string("", [.attachment(attachment)]))
    }
    public func appendInterpolation(image:UIImage?, bounds:CGRect? = nil) {
        let attachment = NSTextAttachment()
        attachment.image = image
        attachment.bounds = bounds ?? CGRect(origin: .zero, size: image?.size ?? .zero)
        self.appendInterpolation(attachment: attachment)
    }
    public func appendInterpolation(image:UIImage?, origin: CGPoint) {
        let attachment = NSTextAttachment()
        attachment.image = image
        attachment.bounds = CGRect(origin: origin, size: image?.size ?? .zero)
        self.appendInterpolation(attachment: attachment)
    }
    public func appendInterpolation(data:Data, type:String) {
         let attachment = NSTextAttachment(data: data, ofType: type)
         self.appendInterpolation(attachment: attachment)
    }
    public func appendInterpolation(file:FileWrapper) {
        let attachment = NSTextAttachment()
        attachment.fileWrapper = file
        self.appendInterpolation(attachment: attachment)
    }
}

extension NSAttributedString {
    public class func create(_ string: AHInterpolationString, _ settings: AHSetting...) -> NSMutableAttributedString {
        string.append(settings: settings)
        return string.attributedString
    }
}
